/*
* Copyright (c) 2006-2007 Erin Catto http:
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked, and must not be
* misrepresented the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
// var b2ContactManager = Class.create();
// Object.extend(b2ContactManager.prototype, b2PairCallback.prototype);
// Object.extend(b2ContactManager.prototype,
import b2PairCallback from '../collision/b2PairCallback';
import b2NullContact from './contacts/b2NullContact';
import b2Contact from './contacts/b2Contact';
export default class b2ContactManager extends b2PairCallback {
    constructor() {
        super();
        this.m_world = null;
        // This lets us provide broadphase proxy pair user data for
        // contacts that shouldn't exist.
        this.m_nullContact = null;
        this.m_destroyImmediate = false;
        // The constructor for b2PairCallback
        // initialize instance variables for references
        this.m_nullContact = new b2NullContact(null, null);
        this.m_world = null;
        this.m_destroyImmediate = false;
    }
    // This is a callback from the broadphase when two AABB proxies begin
    // to overlap. We create a b2Contact to manage the narrow phase.
    PairAdded(proxyUserData1, proxyUserData2) {
        var shape1 = proxyUserData1;
        var shape2 = proxyUserData2;
        var body1 = shape1.m_body;
        var body2 = shape2.m_body;
        if (body1.IsStatic() && body2.IsStatic()) {
            return this.m_nullContact;
        }
        if (shape1.m_body == shape2.m_body) {
            return this.m_nullContact;
        }
        if (body2.IsConnected(body1)) {
            return this.m_nullContact;
        }
        if (this.m_world.m_filter != null && this.m_world.m_filter.ShouldCollide(shape1, shape2) == false) {
            return this.m_nullContact;
        }
        // Ensure that body2 is dynamic (body1 is static or dynamic).
        if (body2.m_invMass == 0.0) {
            var tempShape = shape1;
            shape1 = shape2;
            shape2 = tempShape;
            //b2Math.b2Swap(shape1, shape2);
            var tempBody = body1;
            body1 = body2;
            body2 = tempBody;
            //b2Math.b2Swap(body1, body2);
        }
        // Call the factory.
        var contact = b2Contact.Create(shape1, shape2, this.m_world.m_blockAllocator);
        if (contact == null) {
            return this.m_nullContact;
        }
        else {
            // Insert into the world.
            contact.m_prev = null;
            contact.m_next = this.m_world.m_contactList;
            if (this.m_world.m_contactList != null) {
                this.m_world.m_contactList.m_prev = contact;
            }
            this.m_world.m_contactList = contact;
            this.m_world.m_contactCount++;
        }
        return contact;
    }
    // This is a callback from the broadphase when two AABB proxies cease
    // to overlap. We destroy the b2Contact.
    PairRemoved(proxyUserData1, proxyUserData2, pairUserData) {
        if (pairUserData == null) {
            return;
        }
        var c = pairUserData;
        if (c != this.m_nullContact) {
            //b2Settings.b2Assert(this.m_world.m_contactCount > 0);
            if (this.m_destroyImmediate == true) {
                this.DestroyContact(c);
                c = null;
            }
            else {
                c.m_flags |= b2Contact.e_destroyFlag;
            }
        }
    }
    DestroyContact(c) {
        //b2Settings.b2Assert(this.m_world.m_contactCount > 0);
        // Remove from the world.
        if (c.m_prev) {
            c.m_prev.m_next = c.m_next;
        }
        if (c.m_next) {
            c.m_next.m_prev = c.m_prev;
        }
        if (c == this.m_world.m_contactList) {
            this.m_world.m_contactList = c.m_next;
        }
        // If there are contact points, then disconnect from the island graph.
        if (c.GetManifoldCount() > 0) {
            var body1 = c.m_shape1.m_body;
            var body2 = c.m_shape2.m_body;
            var node1 = c.m_node1;
            var node2 = c.m_node2;
            // Wake up touching bodies.
            body1.WakeUp();
            body2.WakeUp();
            // Remove from body 1
            if (node1.prev) {
                node1.prev.next = node1.next;
            }
            if (node1.next) {
                node1.next.prev = node1.prev;
            }
            if (node1 == body1.m_contactList) {
                body1.m_contactList = node1.next;
            }
            node1.prev = null;
            node1.next = null;
            // Remove from body 2
            if (node2.prev) {
                node2.prev.next = node2.next;
            }
            if (node2.next) {
                node2.next.prev = node2.prev;
            }
            if (node2 == body2.m_contactList) {
                body2.m_contactList = node2.next;
            }
            node2.prev = null;
            node2.next = null;
        }
        // Call the factory.
        b2Contact.Destroy(c, this.m_world.m_blockAllocator);
        --this.m_world.m_contactCount;
    }
    // Destroy any contacts marked for deferred destruction.
    CleanContactList() {
        var c = this.m_world.m_contactList;
        while (c != null) {
            var c0 = c;
            c = c.m_next;
            if (c0.m_flags & b2Contact.e_destroyFlag) {
                this.DestroyContact(c0);
                c0 = null;
            }
        }
    }
    // This is the top level collision call for the time step. Here
    // all the narrow phase collision is processed for the world
    // contact list.
    Collide() {
        var body1;
        var body2;
        var node1;
        var node2;
        for (var c = this.m_world.m_contactList; c != null; c = c.m_next) {
            if (c.m_shape1.m_body.IsSleeping() &&
                c.m_shape2.m_body.IsSleeping()) {
                continue;
            }
            var oldCount = c.GetManifoldCount();
            c.Evaluate();
            var newCount = c.GetManifoldCount();
            if (oldCount == 0 && newCount > 0) {
                //b2Settings.b2Assert(c.GetManifolds().pointCount > 0);
                // Connect to island graph.
                body1 = c.m_shape1.m_body;
                body2 = c.m_shape2.m_body;
                node1 = c.m_node1;
                node2 = c.m_node2;
                // Connect to body 1
                node1.contact = c;
                node1.other = body2;
                node1.prev = null;
                node1.next = body1.m_contactList;
                if (node1.next != null) {
                    node1.next.prev = c.m_node1;
                }
                body1.m_contactList = c.m_node1;
                // Connect to body 2
                node2.contact = c;
                node2.other = body1;
                node2.prev = null;
                node2.next = body2.m_contactList;
                if (node2.next != null) {
                    node2.next.prev = node2;
                }
                body2.m_contactList = node2;
            }
            else if (oldCount > 0 && newCount == 0) {
                // Disconnect from island graph.
                body1 = c.m_shape1.m_body;
                body2 = c.m_shape2.m_body;
                node1 = c.m_node1;
                node2 = c.m_node2;
                // Remove from body 1
                if (node1.prev) {
                    node1.prev.next = node1.next;
                }
                if (node1.next) {
                    node1.next.prev = node1.prev;
                }
                if (node1 == body1.m_contactList) {
                    body1.m_contactList = node1.next;
                }
                node1.prev = null;
                node1.next = null;
                // Remove from body 2
                if (node2.prev) {
                    node2.prev.next = node2.next;
                }
                if (node2.next) {
                    node2.next.prev = node2.prev;
                }
                if (node2 == body2.m_contactList) {
                    body2.m_contactList = node2.next;
                }
                node2.prev = null;
                node2.next = null;
            }
        }
    }
}
